namespace ParserGenerator.SyntacticAnalysis
{
    [global::System.Serializable]
    public sealed partial class Parser
    {
        private int _currentStart;
        private int _pendin;
        private readonly global::System.Collections.Generic.Stack<int> _tabIndent = new global::System.Collections.Generic.Stack<int>();
        private readonly global::System.Collections.Generic.Stack<int> _spaceIndent = new global::System.Collections.Generic.Stack<int>();
        private readonly global::System.Collections.Generic.Stack<int> _savedStarts = new global::System.Collections.Generic.Stack<int>();
        private global::ParserGenerator.Input.SourceContext _nodeContext;
        private readonly global::System.Collections.Generic.List<int> _states = new global::System.Collections.Generic.List<int>();
        private readonly global::System.Collections.Generic.List<string> _symbols = new global::System.Collections.Generic.List<string>();
        private readonly global::System.Collections.Generic.List<object> _values = new global::System.Collections.Generic.List<object>();
        private readonly global::System.Collections.Generic.List<global::ParserGenerator.Input.SourceContext> _contexts = new global::System.Collections.Generic.List<global::ParserGenerator.Input.SourceContext>();

        public global::ParserGenerator.Input.SourceReader Reader { get; private set; }
        public global::ParserGenerator.Input.SourceLocation TokenBegin { get; private set; }
        public global::ParserGenerator.Input.SourceLocation TokenEnd { get; private set; }
        public global::ParserGenerator.Input.SourceContext TokenContext { get; private set; }
        public object TokenValue { get; private set; }
        public int Level { get; private set; }

        public Parser()
            : this(global::ParserGenerator.Input.SourceReader.FromConsole())
        {
        }

        public Parser(global::ParserGenerator.Input.SourceReader reader)
        {
            if (reader == null)
                throw new global::System.ArgumentNullException(nameof(reader));
            Reader = reader;
            TokenBegin = TokenEnd = Reader.Location;
            _tabIndent.Push(0);
            _spaceIndent.Push(0);
        }

        private int PrescanNextToken()
        {
            while (true)
            {
                bool isBlankLine = false;
                if (Reader.Location.Offset == 0)
                {
                    while (IsNonLineTerminatorWhiteSpace(Reader.Peek()))
                        Reader.Read();
                    bool comment = Reader.Peek() == '#';
                    bool lineBegin = Reader.Location.Offset == 0;
                    bool lineEnd = IsLineTerminator(Reader.Peek());
                    isBlankLine = comment || lineEnd && !(lineBegin && Reader.IsInteractive);
                    if (!isBlankLine && Level == 0)
                        CheckIndentation();
                }
                if (_pendin != 0)
                {
                    TokenBegin = Reader.Location;
                    if (_pendin < 0)
                    {
                        _pendin++;
                        return 2;
                    }
                    else
                    {
                        _pendin--;
                        return 1;
                    }
                }
                while (true)
                {
                    while (IsNonLineTerminatorWhiteSpace(Reader.Peek()))
                        Reader.Read();
                    if (Reader.Peek() == '#')
                    {
                        int c;
                        while ((c = Reader.Peek()) != -1)
                            if (IsLineTerminator(c))
                                break;
                            else
                                Reader.Read();
                    }
                    if (Reader.Peek() == -1)
                    {
                        TokenBegin = Reader.Location;
                        return 0;
                    }
                    if (IsLineTerminator(Reader.Peek()))
                    {
                        if (isBlankLine || Level > 0)
                        {
                            Reader.Read();
                            break;
                        }
                        else
                        {
                            TokenBegin = Reader.Location;
                            Reader.Read();
                            return 3;
                        }
                    }
                    if (Reader.Peek() == '\\')
                    {
                        global::ParserGenerator.Input.SourceLocation loc = Reader.Location;
                        Reader.Read();
                        if (IsLineTerminator(Reader.Peek()))
                        {
                            Reader.Read();
                            continue;
                        }
                        else
                        {
                            throw SyntaxError(global::ParserGenerator.SR.InvalidLineContinuation, loc);
                        }
                    }
                    return PrescanNextTokenCallback();
                }
            }
        }

        private void CheckIndentation()
        {
            if (Reader.Location.Offset > _tabIndent.Peek())
            {
                if (Reader.Location.Column > _spaceIndent.Peek())
                {
                    _pendin++;
                    _tabIndent.Push(Reader.Location.Offset);
                    _spaceIndent.Push(Reader.Location.Column);
                }
                else
                {
                    global::ParserGenerator.Exceptions.TabErrorException e = new global::ParserGenerator.Exceptions.TabErrorException(global::ParserGenerator.SR.InconsistentTabSpace);
                    e.AddLocation(Reader.Location);
                    throw e;
                }
            }
            else if (Reader.Location.Offset < _tabIndent.Peek())
            {
                while (_tabIndent.Count > 1 && Reader.Location.Offset < _tabIndent.Peek())
                {
                    _pendin--;
                    _tabIndent.Pop();
                    _spaceIndent.Pop();
                }
                if (Reader.Location.Offset != _tabIndent.Peek())
                {
                    global::ParserGenerator.Exceptions.IndentationErrorException e = new global::ParserGenerator.Exceptions.IndentationErrorException(global::ParserGenerator.SR.MismatchUnindent);
                    e.AddLocation(Reader.Location);
                    throw e;
                }
                if (Reader.Location.Column != _spaceIndent.Peek())
                {
                    global::ParserGenerator.Exceptions.TabErrorException e = new global::ParserGenerator.Exceptions.TabErrorException(global::ParserGenerator.SR.InconsistentTabSpace);
                    e.AddLocation(Reader.Location);
                    throw e;
                }
            }
            else
            {
                if (Reader.Location.Column != _spaceIndent.Peek())
                {
                    global::ParserGenerator.Exceptions.TabErrorException e = new global::ParserGenerator.Exceptions.TabErrorException(global::ParserGenerator.SR.InconsistentTabSpace);
                    e.AddLocation(Reader.Location);
                    throw e;
                }
            }
        }

        private void PushStart(int start)
        {
            _savedStarts.Push(_currentStart);
            _currentStart = start;
        }

        private void GotoStart(int start)
        {
            _currentStart = start;
        }

        private void PopStart()
        {
            _currentStart = _savedStarts.Pop();
        }

        public global::ParserGenerator.Exceptions.SyntaxErrorException SyntaxError(string message, global::ParserGenerator.Input.SourceLocation location)
        {
            if (message == null)
                throw new global::System.ArgumentNullException(nameof(message));
            global::ParserGenerator.Exceptions.SyntaxErrorException e = new global::ParserGenerator.Exceptions.SyntaxErrorException(message);
            e.AddLocation(location);
            return e;
        }

        public global::ParserGenerator.Exceptions.SyntaxErrorException SyntaxError(string message, global::ParserGenerator.Input.SourceContext context)
        {
            if (message == null)
                throw new global::System.ArgumentNullException(nameof(message));
            if (context == null)
                throw new global::System.ArgumentNullException(nameof(context));
            return SyntaxError(message, context.Begin);
        }

        public global::ParserGenerator.Exceptions.SyntaxErrorException SyntaxError(string message)
        {
            if (message == null)
                throw new global::System.ArgumentNullException(nameof(message));
            return SyntaxError(message, Reader.Location);
        }

        private static readonly string[] _starts =
        {
            "DEFAULT"
        };

        private static readonly string[] _tokens =
        {
            "None",
            "Indent",
            "Unindent",
            "NewLine",
            "'('",
            "')'",
            "'*'",
            "'n'"
        };

        private static readonly int[] _start =
        {
            0x00, 0x00, 0x00
        };

        private static readonly int[] _flag =
        {
            0x00, 0x00, 0x45, 0x45, 0x55, 0x55, 0x65, 0x65,
            0x75, 0x75
        };

        private static readonly int[] _fast =
        {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        };

        private static readonly int[] _first =
        {
            0x00, 0x28, 0x29, 0x2A, 0x2B, 0x6E, 0x6F
        };

        private static readonly int[] _last =
        {
            0x00000027, 0x00000028, 0x00000029, 0x0000002A,
            0x0000006D, 0x0000006E, 0x0010FFFF
        };

        private static readonly int[] _map =
        {
            0x00, 0x01, 0x02, 0x03, 0x00, 0x04, 0x00
        };

        private static readonly int[] _sbase =
        {
            0x00, 0x01, 0x01, 0x01, 0x01
        };

        private static readonly int[] _scheck =
        {
            0x05, 0x01, 0x02, 0x03, 0x04, 0x05
        };

        private static readonly int[] _svalue =
        {
            0x00, 0x01, 0x02, 0x03, 0x04
        };

        private static readonly string[] _goals =
        {
            "S"
        };

        private static readonly string[] _nonterminals =
        {
            "S",
            "Primary",
            "Postfix",
            "Unary",
            "Binary",
            "E",
            "name",
            "S'"
        };

        private static readonly string[] _text =
        {
            "end marker",
            "indent",
            "unindent",
            "new line",
            "'('",
            "')'",
            "'*'",
            "'n'"
        };

        private static readonly int[] _goal =
        {
            0x00
        };

        private static readonly int[] _left =
        {
            0x07, 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04,
            0x04, 0x05, 0x06
        };

        private static readonly int[] _length =
        {
            0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x02, 0x01,
            0x03, 0x01, 0x01
        };

        private static readonly int[] _pbase =
        {
            0x30, 0x03, 0x02, 0x16, 0x1D, 0x1F, 0x30, 0x21,
            0x28, 0x30, 0x2A, 0x30, 0x2C, 0x0D, 0x33, 0x35,
            0x05, 0x0F, 0x01, 0x00, 0x10, 0x04, 0x08, 0x06
        };

        private static readonly int[] _pcheck =
        {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06,
            0x00, 0x09, 0x09, 0x0B, 0x0B, 0x09, 0x06, 0x00,
            0x00, 0x09, 0x05, 0x0B, 0x10, 0x06, 0x00, 0x10,
            0x09, 0x09, 0x0B, 0x05, 0x06, 0x00, 0x10, 0x00,
            0x10, 0x00, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
            0x00, 0x10, 0x00, 0x10, 0x00, 0x05, 0x06, 0x05,
            0x06, 0x05, 0x06, 0x00, 0x04, 0x00, 0x06, 0x07,
            0x05, 0x06, 0x05, 0x06, 0x10, 0x10, 0x10, 0x10,
            0x10, 0x10, 0x10, 0x10, 0x10
        };

        private static readonly int[] _pvalue =
        {
            0x0004, 0x0005, 0x0012, 0x0006, 0x0002, 0x0001, 0x000C, 0x0005,
            0x0008, 0x0004, 0x0005, 0x000E, 0x0005, 0x000D, 0x0008, 0x0007,
            0x0003, 0x0008, 0x00F1, 0x0008, 0x0000, 0x0007, 0x0092, 0x0000,
            0x0007, 0x0003, 0x0007, 0x0092, 0x00B1, 0x0072, 0x0000, 0x0052,
            0x0000, 0x0042, 0x0072, 0x0072, 0x0052, 0x0052, 0x0042, 0x0042,
            0x0022, 0x0000, 0x00A2, 0x0000, 0x0062, 0x0022, 0x0022, 0x00A2,
            0x00A2, 0x0062, 0x0062, 0x0082, 0x0091, 0x0032, 0x0061, 0x00A1,
            0x0082, 0x0082, 0x0032, 0x0032
        };

        private static int Map(int c)
        {
            if (0 <= c && c <= _fast.Length)
                return _fast[c];
            int i = 0;
            int j = _map.Length - 1;
            while (i <= j)
            {
                int k = i + (j - i >> 1);
                if (c < _first[k])
                    j = k - 1;
                else if (_last[k] < c)
                    i = k + 1;
                else
                    return _map[c];
            }
            return -1;
        }

        private static int Move(int state, int c)
        {
            int k = Map(c);
            if (k == -1)
                return -1;
            if (_scheck[_sbase[state] + k] == k)
                return _svalue[_sbase[state] + k];
            else
                return -1;
        }

        private static bool IsLineTerminator(int c)
        {
            switch (c)
            {
                case 0x000a: // '\n'
                case 0x000b: // '\v'
                case 0x000c: // '\f'
                case 0x000d: // '\r'
                case 0x0085: // '\u0085'
                case 0x2028: // '\u2028'
                case 0x2029: // '\u2029'
                    return true;
                default:
                    return false;
            }
        }

        private static bool IsNonLineTerminatorWhiteSpace(int c)
        {
            switch (c)
            {
                case 0x0009: // '\t'
                case 0x0020: // ' '
                case 0x00a0: // '\u00a0'
                case 0x1680: // '\u1680'
                case 0x2000: // '\u2000'
                case 0x2001: // '\u2001'
                case 0x2002: // '\u2002'
                case 0x2003: // '\u2003'
                case 0x2004: // '\u2004'
                case 0x2005: // '\u2005'
                case 0x2006: // '\u2006'
                case 0x2007: // '\u2007'
                case 0x2008: // '\u2008'
                case 0x2009: // '\u2009'
                case 0x200a: // '\u200a'
                case 0x202f: // '\u202f'
                case 0x205f: // '\u205f'
                case 0x3000: // '\u3000'
                    return true;
                default:
                    return false;
            }
        }

        private int PrescanNextTokenCallback()
        {
            while (true)
            {
                TokenBegin = TokenEnd = Reader.Location;
                int saved = -1;
                int start = _currentStart;
                int state = _start[start * 3 + (Reader.Location.Offset == 0 ? 0 : 1)];
                while (true)
                {
                    int c = Reader.Read();
                    if (c == -1)
                        break;
                    int result = Move(state, c);
                    if (result == -1)
                        break;
                    int flag = _flag[result * 2];
                    if ((flag & 0x1) != 0)
                    {
                        int flag2 = _flag[result * 2 + 1];
                        bool atEOL = IsLineTerminator(Reader.Peek()) || Reader.Peek() == -1;
                        if ((flag & 0x2) == 0 || atEOL || (flag2 & 0x1) != 0)
                        {
                            TokenEnd = Reader.Location;
                            saved = result;
                        }
                    }
                    state = result;
                }
                if (saved == -1)
                {
                    Reader.Location = TokenBegin;
                    TokenContext = global::ParserGenerator.Input.SourceContext.New(TokenBegin, Reader.Location);
                    switch (start)
                    {
                        default:
                            throw SyntaxError(global::ParserGenerator.SR.InvalidToken);
                    }
                }
                else
                {
                    Reader.Location = TokenEnd;
                    TokenContext = global::ParserGenerator.Input.SourceContext.New(TokenBegin, Reader.Location);
                    int flag = _flag[saved * 2];
                    int flag2 = _flag[saved * 2 + 1];
                    bool atEOL = IsLineTerminator(Reader.Peek()) || Reader.Peek() == -1;
                    int action = (flag & 0x2) == 0 || atEOL ? flag : flag2;
                    if ((action & 0x4) != 0)
                        return action >> 4;
                    int rule = action >> 4;
                    switch (rule)
                    {
                        default:
                            break;
                    }
                }
            }
        }

        public int GetNextToken()
        {
            TokenContext = null;
            TokenValue = null;
            int tag = PrescanNextToken();
            if (TokenContext == null)
                TokenContext = global::ParserGenerator.Input.SourceContext.New(TokenBegin, Reader.Location);
            return tag;
        }

        private static int Action(int state, int c)
        {
            if (_pcheck[_pbase[state] + c] == c)
                return _pvalue[_pbase[state] + c];
            else
                return 0;
        }

        private static int Goto(int state, int c)
        {
            return _pvalue[_pbase[16 + c] + state];
        }

        private global::ParserGenerator.Exceptions.SyntaxErrorException MakeError(int state, int t)
        {
            global::System.Collections.Generic.List<string> tokens = new global::System.Collections.Generic.List<string>();
            for (int c = 0; c < _text.Length; c++)
            {
                int s = Action(state, c);
                if ((s & 0x1) != 0)
                    tokens.Add(_text[c]);
                else if ((s & 0x2) != 0)
                {
                    tokens.Clear();
                    break;
                }
            }
            string message;
            if (tokens.Count == 0)
                message = string.Format(global::ParserGenerator.SR.UnexpectedToken, _text[t]);
            else
            {
                global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder();
                switch (tokens.Count)
                {
                    case 1:
                        sb.Append(tokens[0]);
                        break;
                    case 2:
                        sb.Append(tokens[0]);
                        sb.Append(" or ");
                        sb.Append(tokens[1]);
                        break;
                    case 3:
                        sb.Append(tokens[0]);
                        sb.Append(", ");
                        sb.Append(tokens[1]);
                        sb.Append(", or ");
                        sb.Append(tokens[2]);
                        break;
                    default:
                        sb.Append(tokens[0]);
                        sb.Append(", ");
                        sb.Append(tokens[1]);
                        sb.Append(", ..., or ");
                        sb.Append(tokens[tokens.Count - 1]);
                        break;
                }
                message = string.Format(global::ParserGenerator.SR.ExpectFound, sb.ToString(), _text[t]);
            }
            global::ParserGenerator.Exceptions.SyntaxErrorException e = new global::ParserGenerator.Exceptions.SyntaxErrorException(message);
            e.AddLocation(TokenContext);
            return e;
        }

        private object Reduce(int prod)
        {
            switch (prod)
            {
                case 0:
                    // S' -> S
                    return null;
                case 1:
                    // S -> E
                    return null;
                case 2:
                    // Primary -> name
                    return null;
                case 3:
                    // Primary -> '(' E ')'
                    return null;
                case 4:
                    // Postfix -> Primary
                    return null;
                case 5:
                    // Unary -> Postfix
                    return null;
                case 6:
                    // Unary -> '*' Unary
                    return null;
                case 7:
                    // Binary -> Unary
                    return null;
                case 8:
                    // Binary -> Binary '*' Unary
                    return null;
                case 9:
                    // E -> Binary
                    return null;
                case 10:
                    // name -> 'n'
                    return null;
                default:
                    return null;
            }
        }

        private object ParseGoal(int goal)
        {
            int state = _goal[goal];
            _states.Add(state);
            int t = GetNextToken();
            while (true)
            {
                int s = Action(state, t);
                if ((s & 0x1) != 0)
                {
                    state = s >> 4;
                    _states.Add(state);
                    _symbols.Add(_tokens[t]);
                    _values.Add(TokenValue);
                    _contexts.Add(TokenContext);
                    t = GetNextToken();
                }
                else if ((s & 0x2) != 0)
                {
                    int prod = s >> 4;
                    int left = _left[prod];
                    int length = _length[prod];
                    if (length == 0)
                        _nodeContext = global::ParserGenerator.Input.SourceContext.New(TokenContext.Begin, TokenContext.Begin);
                    else
                        _nodeContext = global::ParserGenerator.Input.SourceContext.New(_contexts[_contexts.Count - length].Begin, _contexts[_contexts.Count - 1].End);
                    if ((s & 0x4) != 0)
                    {
                        object result = _values[0];
                        _states.Clear();
                        _symbols.Clear();
                        _values.Clear();
                        _contexts.Clear();
                        return result;
                    }
                    else
                    {
                        object @value = Reduce(prod);
                        if (length > 0)
                        {
                            _states.RemoveRange(_states.Count - length, length);
                            _symbols.RemoveRange(_symbols.Count - length, length);
                            _values.RemoveRange(_values.Count - length, length);
                            _contexts.RemoveRange(_contexts.Count - length, length);
                        }
                        state = Goto(_states[_states.Count - 1], left);
                        _states.Add(state);
                        _symbols.Add(_nonterminals[left]);
                        _values.Add(@value);
                        _contexts.Add(_nodeContext);
                    }
                }
                else
                {
                    throw MakeError(state, t);
                }
            }
        }

        public void Parse()
        {
            ParseS();
        }

        public void ParseS()
        {
            ParseGoal(0);
        }

        public static void TestTokenizer()
        {
            while (true)
            {
                global::ParserGenerator.Input.SourceReader reader = global::ParserGenerator.Input.SourceReader.FromConsole();
                if (reader.Peek() == -1)
                    break;
                try
                {
                    global::ParserGenerator.SyntacticAnalysis.Parser tokenizer = new global::ParserGenerator.SyntacticAnalysis.Parser(reader);
                    while (true)
                    {
                        int tag = tokenizer.GetNextToken();
                        global::System.Console.WriteLine("{0}: {1} {2}", tokenizer.TokenContext.Begin, _tokens[tag], tokenizer.TokenContext);
                        if (tag == 0)
                            break;
                    }
                }
                catch (global::System.Exception e)
                {
                    global::ParserGenerator.Extensions.ExceptionExtensions.Print(e);
                }
            }
        }

        public static void Test()
        {
            while (true)
            {
                global::ParserGenerator.Input.SourceReader reader = global::ParserGenerator.Input.SourceReader.FromConsole();
                if (reader.Peek() == -1)
                    break;
                try
                {
                    global::ParserGenerator.SyntacticAnalysis.Parser parser = new global::ParserGenerator.SyntacticAnalysis.Parser(reader);
                    parser.Parse();
                }
                catch (global::System.Exception e)
                {
                    global::ParserGenerator.Extensions.ExceptionExtensions.Print(e);
                }
            }
        }
    }
}
